package VLine

import (
	"fmt"
	"math"
)

type Line struct {
	constants    []interface{}
	instructions []*Instruction
	functions    []*Function
	cursor       int
	register     []interface{}
	upvalue      map[string]interface{}
	result       interface{}
}

func LineNew(constants []interface{}, instructions []*Instruction, function []*Function, size int) *Line {
	var line = &Line{}
	line.constants = constants
	line.instructions = instructions
	line.functions = function
	line.cursor = 0
	line.register = make([]interface{}, size)
	return line
}

func (line *Line) getValue(index int) interface{} {
	if index < 0 {
		index = -index - 1
		return line.constants[index]
	}
	return line.register[index]
}

func (line *Line) Run() *Line {
	for line.cursor < len(line.instructions) {
		var instruction = line.instructions[line.cursor]
		switch instruction.code {
		case VM_LOAD:
			line.register[instruction.args[0]] = line.getValue(instruction.args[1])
		case VM_MOV:
			line.register[instruction.args[0]] = line.getValue(instruction.args[1])
		case VM_NEWTABLE:
			line.register[instruction.args[0]] = TableNew(instruction.args[1], instruction.args[2])
		case VM_GETTV:
			switch value := line.getValue(instruction.args[1]).(type) {
			case *Table:
				line.register[instruction.args[0]] = value.Get(line.getValue(instruction.args[2]))
			case string:
				line.register[instruction.args[0]] = string(value[(int64)(line.getValue(instruction.args[2]).(float64))])
			default:
				panic(fmt.Sprint("不能获取", value, "[", line.getValue(instruction.args[2]), "]", "的值"))
			}
		case VM_SETTV:
			var table = line.getValue(instruction.args[0]).(*Table)
			switch key := line.getValue(instruction.args[1]).(type) {
			case string:
				table.hashmap[key] = line.getValue(instruction.args[2])
			case float64:
				table.list.Set(int(key), line.getValue(instruction.args[2]))
			default:
				panic(fmt.Sprint(key, "不能作为键值"))
			}

		case VM_POS:
			line.register[instruction.args[0]] = +line.getValue(instruction.args[1]).(float64)

		case VM_NEG:
			line.register[instruction.args[0]] = -line.getValue(instruction.args[1]).(float64)

		case VM_ADD:
			line.register[instruction.args[0]] = line.getValue(instruction.args[2]).(float64) + line.getValue(instruction.args[1]).(float64)

		case VM_SUB:
			line.register[instruction.args[0]] = line.getValue(instruction.args[2]).(float64) - line.getValue(instruction.args[1]).(float64)

		case VM_MUL:
			line.register[instruction.args[0]] = line.getValue(instruction.args[2]).(float64) * line.getValue(instruction.args[1]).(float64)

		case VM_DIV:
			line.register[instruction.args[0]] = line.getValue(instruction.args[2]).(float64) / line.getValue(instruction.args[1]).(float64)

		case VM_MOD:
			line.register[instruction.args[0]] = (float64)((int64)(line.getValue(instruction.args[2]).(float64)) % (int64)(line.getValue(instruction.args[1]).(float64)))

		case VM_POWER:
			line.register[instruction.args[0]] = math.Pow(line.getValue(instruction.args[2]).(float64), line.getValue(instruction.args[1]).(float64))
		case VM_UNM:
			line.register[instruction.args[0]] = -line.register[instruction.args[0]].(float64)

		case VM_NOT:
			line.register[instruction.args[0]] = !line.register[instruction.args[0]].(bool)

		case VM_EQ:
			var result = line.getValue(instruction.args[1]) == line.getValue(instruction.args[2])
			line.register[instruction.args[0]] = result
		case VM_NEQ:
			var result = line.getValue(instruction.args[1]) != line.getValue(instruction.args[2])
			line.register[instruction.args[0]] = result

		case VM_LT:
			var result = line.getValue(instruction.args[2]).(float64) < line.getValue(instruction.args[1]).(float64)
			line.register[instruction.args[0]] = result

		case VM_LE:
			var result = line.getValue(instruction.args[2]).(float64) <= line.getValue(instruction.args[1]).(float64)
			line.register[instruction.args[0]] = result

		case VM_AND:
			var result = line.getValue(instruction.args[1]).(bool) && line.getValue(instruction.args[2]).(bool)
			line.register[instruction.args[0]] = result

		case VM_OR:
			var result = line.getValue(instruction.args[1]).(bool) || line.getValue(instruction.args[2]).(bool)
			line.register[instruction.args[0]] = result

		case VM_JUMP:
			if len(instruction.args) == 1 {
				line.cursor = instruction.args[0] - 1
			} else {
				if instruction.args[1] == 0 && !line.getValue(instruction.args[0]).(bool) {
					line.cursor = instruction.args[2] - 1

				} else if instruction.args[1] == 1 && line.getValue(instruction.args[0]).(bool) {
					line.cursor = instruction.args[2] - 1
				}
			}

		case VM_CONCAT:
			line.register[instruction.args[0]] = line.getValue(instruction.args[1]).(string) + line.getValue(instruction.args[2]).(string)

		case VM_RETURN:
			if len(instruction.args) == 0 {
				line.result = nil
			} else {
				line.result = line.getValue(instruction.args[0])
			}
			line.cursor = len(line.instructions) + 1
		case VM_CALL:
			var result interface{}
			var args = line.register[instruction.args[3] : instruction.args[3]+instruction.args[4]]
			switch body := line.getValue(instruction.args[2]).(type) {
			case func(line *Line, self interface{}, args []interface{}) interface{}:
				result = body(line, line.getValue(instruction.args[1]), args)
			case *Function:
				body.Call(line, line.getValue(instruction.args[1]), args)
				result = body.result
			default:
				panic(fmt.Sprint(body, "不能被执行"))
			}
			line.register[instruction.args[0]] = result
		case VM_CALLX:
			var result interface{}
			switch body := line.getValue(instruction.args[2]).(type) {
			case func(line *Line, self interface{}, args *Table) interface{}:
				result = body(line, line.getValue(instruction.args[1]), line.getValue(instruction.args[3]).(*Table))
			case *Function:
				body.CallX(line, line.getValue(instruction.args[1]), line.getValue(instruction.args[3]).(*Table))
				result = body.result
			default:
				panic(fmt.Sprint(body, "不能被执行"))
			}
			line.register[instruction.args[0]] = result
		case VM_PUSH:
			line.getValue(instruction.args[0]).(*Table).list.Push(line.getValue(instruction.args[1]))

		case VM_IMPORT:
			if len(instruction.args) == 2 {
				line.register[instruction.args[0]] = Base[line.getValue(instruction.args[1]).(string)]
			}
		case VM_CLOSURE:
			var function = line.functions[instruction.args[1]-0xffff-1]
			for k, v := range function.layer.imports {
				function.upvalue[k] = line.register[v]
			}
			line.register[instruction.args[0]] = function
		case VM_GETUPVAL:
			line.register[instruction.args[0]] = line.upvalue[line.getValue(instruction.args[1]).(string)]
		case VM_LOOP:
			switch value := line.register[instruction.args[1]].(type) {
			case *Table:

			case string:
				line.register[instruction.args[0]] = LoopStringNew(value)
			case Loop:
				line.register[instruction.args[0]] = line.register[instruction.args[1]]
			}
		case VM_FORIN:
			var value = line.register[instruction.args[1]].(Loop)
			if !value.HasNext() {
				line.cursor = instruction.args[2] - 1
			} else {
				line.register[instruction.args[0]] = value.Next()
			}
		}
		line.cursor++
	}
	return line
}
